package org.xtext.runtime.plan;

import java.util.Map;
import java.util.Set;

import org.xtext.runtime.formula.Predicate;
import org.xtext.runtime.interpreter.Agent;
import org.xtext.runtime.interpreter.FailedActionException;
import org.xtext.runtime.interpreter.NoSuchActionException;
import org.xtext.runtime.library.LibraryWrapper;
import org.xtext.runtime.term.Variable;
import org.xtext.runtime.util.Bindings;
import org.xtext.runtime.util.EvaluateTermsVisitor;

public class LibraryActionStep implements IPlanStep {
	private String moduleId;
	private Predicate operation;

	private class Executor implements Runnable {
		Agent agent;
		PlanStack planStack;
		LibraryWrapper wrapper;

		public Executor(Agent agent, LibraryWrapper wrapper, PlanStack planStack) {
			this.wrapper = wrapper;
			this.agent = agent;
			this.planStack = planStack;
		}

		@Override
		public void run() {
			long start = agent.getNow();
			boolean result = false;
			try {
				result = wrapper.invokeAction((Predicate) operation.accept(new EvaluateTermsVisitor(agent)));
			} catch (FailedActionException e) {
				e.printStackTrace();
			} catch (NoSuchActionException e1) {
				e1.printStackTrace();
			}
			
			long duration = agent.getNow() - start;
			if (result) {
				agent.logAction(moduleId + "." + operation + " [SUCCESS: " + duration +"ms]");
				agent.notify(planStack, true);
			} else {
				agent.logAction(moduleId + "." + operation + " [FAILED: " + duration +"ms]");
				agent.notify(planStack, false);
			}
		}
	}
	
	public LibraryActionStep(String module, Predicate operation) {
		this.moduleId = module;
		this.operation = operation;
	}
	
	@Override
	public LibraryActionStep apply(Bindings set) {
		operation = operation.apply(set);
		return this;
	}

	@Override
	public void handle(Agent agent, PlanStack planStack) {
		LibraryWrapper wrapper = agent.getLibrary(moduleId);
		if (wrapper == null) {
			System.err.println(toString("") + " [NO SUCH LIBRARY: " + moduleId + "]");
			agent.logAction(toString("") + " [NO SUCH LIBRARY: " + moduleId + "]");
			planStack.fail();
			return;
		}
		
		planStack.suspend();
		new Thread(new Executor(agent, wrapper, planStack), moduleId + "." + operation).start();
	}

	@Override
	public String toString(String tabs) {
		return tabs + moduleId + "." + operation;
	}
	
	public String toFullString(String tabs) {
		return toString(tabs);
	}
	
	@Override
	public Set<Variable> variables() {
		return this.operation.variables();
	}

	@Override
	public IPlanStep copy(Map<String, Variable> vars) {
		return new LibraryActionStep(moduleId, operation.copy(vars));
	}

	@Override
	public void reset() {
	}
}
